#ifdef __cplusplus
extern "C"{
#endif

#include "xs_def.h"
#include "xs_io.h"
#include "xs_ev.h"
#include "xs_log.h"
#include "xs_network.h"
#include "xs_malloc.h"
#include "xs_sock.h"
#include "xs_string.h"
#include "xs_memory.h"

#ifdef WIN32
int xs_inet_pton(int __a, const char *__s, void *__d)
{
    if(__a == AF_INET6)
        assert(0);

    struct	in_addr* addr = (struct in_addr*)__d;
    addr->S_un.S_addr = inet_addr(__s);

    return 0;
}
#else
int xs_inet_pton(int __a, const char * __s, void * __d)
{
	#include <arpa/inet.h>
	return (inet_pton(__a, __s, __d));
}
#endif

const char *
xs_inet_ntop (int family, const void *addrptr, char *strptr, size_t len)
{
	unsigned char *p = (unsigned char *) addrptr;

	if (family == AF_INET) 
	{
		char temp[INET_ADDRSTRLEN];

		xs_snprintf(temp, sizeof(temp), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);

		if (xs_strlen(temp) >= len) 
		{
			errno = ENOSPC;
			return NULL;
		}
		xs_strcpy(strptr, temp);
		return strptr;
	}

	errno = EAFNOSUPPORT;
	return NULL;
}
int xs_str2sockunion (char *str, union sockunion *su)
{
	int ret;

	memset (su, 0, sizeof (union sockunion));

	ret = xs_inet_pton (AF_INET, str, &su->sin.sin_addr);
	if (ret > 0)			/* Valid IPv4 address format. */
	{
		su->sin.sin_family = AF_INET;
#ifdef HAVE_SIN_LEN
		su->sin.sin_len = sizeof(struct sockaddr_in);
#endif /* HAVE_SIN_LEN */
		return 0;
	}
#ifdef HAVE_IPV6
	ret = xs_inet_pton (AF_INET6, str, &su->sin6.sin6_addr);
	if (ret > 0)			/* Valid IPv6 address format. */
	{
		su->sin6.sin6_family = AF_INET6;
#ifdef SIN6_LEN
		su->sin6.sin6_len = sizeof(struct sockaddr_in6);
#endif /* SIN6_LEN */
		return 0;
	}
#endif /* HAVE_IPV6 */
	return -1;
}

union sockunion *
xs_sockunion_str2su (char *str)
{
	int ret;
	union sockunion *su;

	su = XMALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
	xs_memset (su, 0, sizeof (union sockunion));

	ret = xs_inet_pton (AF_INET, str, &su->sin.sin_addr);
	if (ret > 0)			/* Valid IPv4 address format. */
	{
		su->sin.sin_family = AF_INET;
#ifdef HAVE_SIN_LEN
		su->sin.sin_len = sizeof(struct sockaddr_in);
#endif /* HAVE_SIN_LEN */
		return su;
	}
#ifdef HAVE_IPV6
	ret = xs_inet_pton (AF_INET6, str, &su->sin6.sin6_addr);
	if (ret > 0)			/* Valid IPv6 address format. */
	{
		su->sin6.sin6_family = AF_INET6;
#ifdef SIN6_LEN
		su->sin6.sin6_len = sizeof(struct sockaddr_in6);
#endif /* SIN6_LEN */
		return su;
	}
#endif /* HAVE_IPV6 */

	XFREE (MTYPE_SOCKUNION, su);
	return NULL;
}

char *
xs_sockunion_su2str (union sockunion *su)
{
	char str[INET6_ADDRSTRLEN];

	switch (su->sa.sa_family)
	{
		case AF_INET:
			xs_inet_ntop (AF_INET, &su->sin.sin_addr, str, sizeof (str));
			break;
#ifdef HAVE_IPV6
		case AF_INET6:
			xs_inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, sizeof (str));
			break;
#endif /* HAVE_IPV6 */
	}
	return xs_strdup (str);
}
const char * xs_sockunion2str (union sockunion *su, char *buf, size_t len)
{
  if  (su->sa.sa_family == AF_INET)
    return xs_inet_ntop (AF_INET, &su->sin.sin_addr, buf, len);
#ifdef HAVE_IPV6
  else if (su->sa.sa_family == AF_INET6)
    return xs_inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len);
#endif /* HAVE_IPV6 */
  return NULL;
}


void xs_sock_close(xs_ev_sock_t* sev)
{
    xs_logi("socket fd=%d closed", sev->fd);
    xs_close_socket(sev->fd);
    xs_free(sev->ev);
}
struct in_addr xs_sock_getip(const char* name)
{
    struct addrinfo hints;
    struct addrinfo* res; /*, *cur;*/
    struct sockaddr_in* addr;
    struct in_addr ret;
    int result;
    ret.s_addr = INADDR_NONE;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family     = AF_INET;
    hints.ai_flags      = AI_PASSIVE;
    hints.ai_protocol   = 0;
    hints.ai_socktype   = SOCK_STREAM;

    result = getaddrinfo(name, NULL, &hints, &res);
    if(result < 0)
    {
        return ret;
    }

    addr = (struct sockaddr_in*)res->ai_addr;
    ret = addr->sin_addr;
    freeaddrinfo(res);
    return ret;
}
int xs_sock_connect(uint16_t port, const char* ip)
{
    int fd;
    int ret;
    struct sockaddr_in addr;

    fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd < 0)
    {
        xs_logd("create error");
        return fd;
    }

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr = xs_sock_getip(ip);

    ret = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0 && errno != EINPROGRESS)
    {
        xs_loge("connect error %d", errno);
        xs_close_socket(fd);
        return ret;
    }
    return fd;
}

int xs_sock_send_block(XSOCKET fd, char* buf, int size, int timeout)
{
    int ret;
    int io = 0;
    fd_set set;
    struct timeval tv;
    
    if(timeout == 0) timeout = 60000;
    tv.tv_sec = timeout/1000;
    tv.tv_usec = timeout%1000*1000;
    
    while(io < size)
    {
        FD_ZERO(&set);
        FD_SET(fd, &set);

        if(timeout >= 0)
            ret = select(fd+1, NULL, &set, NULL, &tv);
        else
            ret = select(fd+1, NULL, &set, NULL, NULL);

        if(ret > 0)
        {
            ret = write(fd, buf+io, size-io);
            if(ret > 0)
            {
                io += ret;
                continue;
            }
            if(ret < 0 && errno == EAGAIN)
                continue;
        }

        break;
    }
    return io;
}

int xs_sock_recv_block(XSOCKET fd, void* buf, int size, int timeout)
{
    int ret;
    int io = 0;
    fd_set set;
    struct timeval tv;
    if(timeout == 0) timeout = 60000;
    tv.tv_sec = timeout/1000;
    tv.tv_usec = timeout%1000*1000;
    
    while(io < size)
    {
        FD_ZERO(&set);
        FD_SET(fd, &set);

        if(timeout >= 0)
            ret = select(fd+1, &set, NULL, NULL, &tv);
        else
            ret = select(fd+1, &set, NULL, NULL, NULL);
		
        if(ret > 0)
        {
            ret = read(fd, buf+io, size-io);
            if(ret > 0)
            {
                io += ret;
                xs_logd("io=%d", io);
                continue;
            }
            if(ret < 0 && errno == EAGAIN)
                continue;
        }
        break;
    }
    return io;
}
#ifdef __cplusplus
}
#endif
